package com.jiaqiang.chatgpt.interfaces;

import com.jiaqiang.chatgpt.application.IWeiXinValidateService;

import com.jiaqiang.chatgpt.domain.receive.model.MessageTextEntity;
import com.jiaqiang.chatgpt.infrastructure.util.XmlBeanUtil;


import com.jiaqiang.chatgpt.common.Constants;
import com.jiaqiang.chatgpt.domain.chat.ChatCompletionRequest;
import com.jiaqiang.chatgpt.domain.chat.ChatCompletionResponse;
import com.jiaqiang.chatgpt.domain.chat.Message;
import com.jiaqiang.chatgpt.session.Configuration;
import com.jiaqiang.chatgpt.session.OpenAiSession;
import com.jiaqiang.chatgpt.session.OpenAiSessionFactory;
import com.jiaqiang.chatgpt.session.defaults.DefaultOpenAiSessionFactory;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 微信公众号，请求处理服务
 */
@Component
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WeiXinPortalController {

    private final Logger logger = LoggerFactory.getLogger(WeiXinPortalController.class);

    @Value("${wx.config.originalid:gh_ac3e9ab08416}")
    private String originalId;

    @Value("${wx.config.apiKey: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}")
    private String apiKey;

    @Value("${wx.config.token:b8b6}")
    private String token;

    @Value("${wx.config.apiHost:https://api.openai.com/}")
    private String apiHost;

    @Resource
    private IWeiXinValidateService weiXinValidateService;
    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    private OpenAiSession openAiSession;
    // todo 将 map 弄成redis.
    private final Map<String, String> chatGPTMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        // 1. 配置文件【可以联系小傅哥获取开发需要的 apihost、apikey】
        Configuration configuration = new Configuration();
        configuration.setApiHost(apiHost);
        logger.info(apiKey);
        logger.info(originalId);
        logger.info(apiHost);
        configuration.setApiKey(apiKey);
        // 废弃属性，后续不在使用
        configuration.setAuthToken(token);
        // 2. 会话工厂
        OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
        // 3. 开启会话
        this.openAiSession = factory.openSession();
        logger.info("开始 openAiSession");
    }


    /**
     * 处理微信服务器发来的get请求，进行签名的验证【付费的 natapp 对接稳定，3块钱域名，9块钱渠道费】
     * http://xfg.nat300.top/wx/portal/wx470537fb2f5bf897
     * <p>
     * appid     微信端AppID
     * signature 微信端发来的签名
     * timestamp 微信端发来的时间戳
     * nonce     微信端发来的随机字符串
     * echostr   微信端发来的验证字符串
     * produces 用于指定处理方法的响应内容类型。在这里，它指定了返回的内容类型为"text/plain"，并且字符编码为"utf-8"。
     */
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String validate(@PathVariable String appid,
                           @RequestParam(value = "signature", required = false) String signature,
                           @RequestParam(value = "timestamp", required = false) String timestamp,
                           @RequestParam(value = "nonce", required = false) String nonce,
                           @RequestParam(value = "echostr", required = false) String echostr) {

        try {
            logger.info("微信公众号验签信息{}开始 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr);
            if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
                throw new IllegalArgumentException("请求参数非法，请核实!");
            }
            // 调用 微信公众号验签服务
            boolean check = weiXinValidateService.checkSign(signature, timestamp, nonce);
            logger.info("微信公众号验签信息 {} 完成 check: {}", appid, check);
            if (!check) {
                return null;
            }
            return echostr;
        } catch (Exception e) {
            logger.error("微信公众号验签信息{}失败 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr, e);
            return null;
        }
    }

    /**
     * 此处是处理微信服务器的消息转发的
     */
    // todo 对询问进加锁  redis setnx userId(openId) 实现
    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String post(@PathVariable String appid,
                       @RequestBody String requestBody,
                       @RequestParam("signature") String signature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce,
                       @RequestParam("openid") String openid,
                       @RequestParam(name = "encrypt_type", required = false) String encType,
                       @RequestParam(name = "msg_signature", required = false) String msgSignature) {

        try {
            logger.info("接收微信公众号信息请求 {} 开始 {}", openid, requestBody);
            // 微信公众号发来的数据是 xml 格式的, 调用工具类进行解析
            MessageTextEntity message = XmlBeanUtil.xmlToBean(requestBody, MessageTextEntity.class);
            // 异步任务  .trim() 很重要!!
            if (chatGPTMap.get(message.getContent().trim()) == null || "NULL".equals(chatGPTMap.get(message.getContent().trim()))) {
                // 第一次发, 反馈信息[文本]
                MessageTextEntity res = new MessageTextEntity();
                res.setToUserName(openid);
                res.setFromUserName(originalId);
                res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));
                res.setMsgType("text");
                res.setContent("消息处理中，请稍后再回复我一句【" + message.getContent().trim() + "】");
                if (chatGPTMap.get(message.getContent().trim()) == null) {
                    doChatGPTTask(message.getContent().trim());
                }
                // 将数据转为 xml 再返回给微信公众号, 展示给用户
                return XmlBeanUtil.beanToXml(res);
            }
            String result = getResultText(openid, chatGPTMap.get(message.getContent().trim()));
            logger.info("接收微信公众号信息请求{}完成 {}", openid, result);
            chatGPTMap.remove(message.getContent().trim());
            return result;
        } catch (Exception e) {
            logger.error("接收微信公众号信息请求{}失败 {}", openid, requestBody, e);
            return getResultText(openid, "服务器异常, 请重试");
        }
    }

    @Nullable
    private String getResultText(String openid, String message) {
        // 反馈信息[文本]
        MessageTextEntity res = new MessageTextEntity();
        res.setToUserName(openid);
        res.setFromUserName(originalId);
        res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));
        res.setMsgType("text");
        res.setContent(message);
        return XmlBeanUtil.beanToXml(res);
    }

    /**
     * 模型更换，调整为 doChatGPTTask02 调用。对象的 pom.xml 中的 chatgpt-sdk-java 版本调整为 1.0 【此版本可以直接从maven仓库拉取，也可以从代码库自己 install 构建 https://gitcode.net/KnowledgePlanet/chatgpt/chatgpt-sdk-java】
     */
//    @Deprecated
    public void doChatGPTTask(String content) {
        // 用于占位, 避免重复处理
        chatGPTMap.put(content, "NULL");
        logger.info("开始询问chatgpt: {}", content);
        try {
            taskExecutor.execute(() -> {
                // OpenAI 请求
                // 1. 创建参数
                ChatCompletionRequest chatCompletion = ChatCompletionRequest
                        .builder()
                        .messages(Collections.singletonList(Message.builder().role(Constants.Role.USER).content(content).build()))
                        .model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode())
                        .build();
                // 2. 发起请求
                ChatCompletionResponse chatCompletionResponse = openAiSession.completions(chatCompletion);
                // 3. 解析结果
                StringBuilder messages = new StringBuilder();
                chatCompletionResponse.getChoices().forEach(e -> {
                    messages.append(e.getMessage().getContent());
                });
                // 更新上面的占位 NULL
                logger.info("询问结束chatgpt: {}", messages.toString());
                chatGPTMap.put(content, messages.toString());
            });
        } catch (Exception e) {
            // 关闭ExecutorService来取消任务
            logger.error("询问chatgpt出错: ", e);
            chatGPTMap.put(content, "NULL");
        }
    }


}
